﻿using AdornerPopup.Core.Controls;
using AdornerPopup.Core.Helpers;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace AdornerPopup.Core.Controls
{
    [DefaultProperty("Child")]
    [ContentProperty("Child")]
    public class AdornerPopup : Control
    {
        #region Fields
        private Window _adornerOfWindow = null;
        private AdornerLayer _adornerLayer = null;
        private FrameworkElementAdorner _adorner = null;
        #endregion

        #region Constructors
        static AdornerPopup()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(AdornerPopup), new FrameworkPropertyMetadata(typeof(AdornerPopup)));
        }
        public AdornerPopup()
        {
            this.Focusable = false;

            this.DataContextChanged += new DependencyPropertyChangedEventHandler(AdornerPopup_DataContextChanged);
        }


        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty ChildProperty =
            DependencyProperty.Register("Child", typeof(FrameworkElement), typeof(AdornerPopup), new PropertyMetadata(null));

        public static readonly DependencyProperty IsOpenProperty =
            DependencyProperty.Register("IsOpen", typeof(bool), typeof(AdornerPopup), new PropertyMetadata(false, IsOpenCallback));

        public static readonly DependencyProperty PlacementTargetProperty =
            DependencyProperty.Register("PlacementTarget", typeof(FrameworkElement), typeof(AdornerPopup), new PropertyMetadata(null, PlacementTargetChangedCallback));

        public static readonly DependencyProperty StaysOpenProperty =
            DependencyProperty.Register("StaysOpen", typeof(bool), typeof(AdornerPopup), new PropertyMetadata(true, StaysOpenCallback));

        public static readonly DependencyProperty IgnoreTargetEventProperty =
            DependencyProperty.Register("IgnoreTargetEvent", typeof(bool), typeof(AdornerPopup), new PropertyMetadata(false));

        public static readonly DependencyProperty HorizontalOffsetProperty =
            DependencyProperty.Register("HorizontalOffset", typeof(double), typeof(AdornerPopup), new PropertyMetadata(0.0));

        public static readonly DependencyProperty VerticalOffsetProperty =
            DependencyProperty.Register("VerticalOffset", typeof(double), typeof(AdornerPopup), new PropertyMetadata(0.0));

        public static readonly DependencyProperty PlacementProperty =
            DependencyProperty.Register("Placement", typeof(AdornerPopupPlacement), typeof(AdornerPopup), new PropertyMetadata(AdornerPopupPlacement.Bottom));

        #endregion

        #region Properties
        /// <summary>
        /// Popup
        /// </summary>
        public FrameworkElement Child
        {
            get { return (FrameworkElement)GetValue(ChildProperty); }
            set { SetValue(ChildProperty, value); }
        }

        public FrameworkElement PlacementTarget
        {
            get { return (FrameworkElement)GetValue(PlacementTargetProperty); }
            set { SetValue(PlacementTargetProperty, value); }
        }

        /// <summary>
        /// 是否打开Popup
        /// </summary>
        public bool IsOpen
        {
            get { return (bool)GetValue(IsOpenProperty); }
            set { SetValue(IsOpenProperty, value); }
        }

        /// <summary>
        /// 鼠标点击非Popup内容时是否隐藏
        /// </summary>
        public bool StaysOpen
        {
            get { return (bool)GetValue(StaysOpenProperty); }
            set { SetValue(StaysOpenProperty, value); }
        }

        /// <summary>
        /// 在StaysOpen为False的情况下, 当点击PlacementTarget对象时忽略鼠标点击不关闭Popup
        /// </summary>
        public bool IgnoreTargetEvent
        {
            get { return (bool)GetValue(IgnoreTargetEventProperty); }
            set { SetValue(IgnoreTargetEventProperty, value); }
        }

        public double HorizontalOffset
        {
            get { return (double)GetValue(HorizontalOffsetProperty); }
            set { SetValue(HorizontalOffsetProperty, value); }
        }

        public double VerticalOffset
        {
            get { return (double)GetValue(VerticalOffsetProperty); }
            set { SetValue(VerticalOffsetProperty, value); }
        }

        public AdornerPopupPlacement Placement
        {
            get { return (AdornerPopupPlacement)GetValue(PlacementProperty); }
            set { SetValue(PlacementProperty, value); }
        }

        #endregion

        #region Event Handlers

        private static void PlacementTargetChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AdornerPopup uc = d as AdornerPopup;
            if (uc == null)
                return;

            uc.ReleasePlacementTargetEventHandler(e.OldValue as FrameworkElement);
            uc.RegisterPlacementTargetEventHandler(e.NewValue as FrameworkElement);
        }

        private void OnPlacementTarget_LostFocus(object sender, RoutedEventArgs e)
        {
            if (StaysOpen)
                return;
            if (this.IsOpen)
            {
                Point pos = Mouse.GetPosition(_adorner);
                HitTestResult hitResult = VisualTreeHelper.HitTest(_adorner, pos);

                if (hitResult == null)
                {
                    IsOpen = false;
                    return;
                }

                // 如果点击对象对Child则返回
                if (TreeHelper.IsDescendantOf(hitResult.VisualHit, _adorner))
                {
                    return;
                }

                pos = Mouse.GetPosition(PlacementTarget);
                hitResult = VisualTreeHelper.HitTest(PlacementTarget, pos);

                // 如果点击对象PlacementTarget则返回
                if (IgnoreTargetEvent && TreeHelper.IsDescendantOf(hitResult.VisualHit, PlacementTarget))
                {
                    return;
                }

                IsOpen = false;
            }
        }

        private static void IsOpenCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AdornerPopup uc = d as AdornerPopup;
            if (uc == null)
                return;

            if (uc.PlacementTarget == null)
            {
                throw new InvalidOperationException("PlacementTarget is null");
            }

            bool isShow = (bool)e.NewValue;
            if (isShow)
            {
                uc.ShowPopup();
            }
            else
            {
                uc.HidePopup();
            }
        }

        private static void StaysOpenCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AdornerPopup uc = d as AdornerPopup;
            if (uc == null)
                return;

            bool newValue = (bool)e.NewValue;

            if (newValue)
            {

                // 如果当前Popup正在和显示中释放鼠标事件
                if (uc.IsOpen)
                {
                    uc.ReleaseMouseDownHandler();
                }
            }
            else
            {
                if (uc.IsOpen)
                {
                    uc.RegisterMouseDownHandler();
                }
            }

        }

        private void AdornerPopup_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            UpdateAdornerDataContext();
        }

        private void OnAdornerOfWindow_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (StaysOpen)
                return;

            if (_adorner == null)
                return;
            Point pos = e.GetPosition(_adorner);
            HitTestResult hitResult = VisualTreeHelper.HitTest(_adorner, pos);

            if (hitResult == null)
            {
                IsOpen = false;
                return;
            }

            // 如果点击对象对Child则返回
            if (TreeHelper.IsDescendantOf(hitResult.VisualHit, _adorner))
            {
                return;
            }

            // 如果点击对象PlacementTarget则返回
            if (IgnoreTargetEvent && TreeHelper.IsDescendantOf(hitResult.VisualHit, PlacementTarget))
            {
                return;
            }

            IsOpen = false;
            e.Handled = true;
        }

        #endregion

        #region Private Methods
        private void ShowPopup()
        {
            if (_adornerLayer == null)
            {
                _adornerLayer = AdornerLayer.GetAdornerLayer(PlacementTarget);
            }

            if (_adornerLayer != null)
            {
                _adorner = new FrameworkElementAdorner(this.Child, PlacementTarget, this, HorizontalOffset, VerticalOffset);
                _adornerLayer.Add(_adorner);

                ScaleTransform scale = null;
                this.Child.RenderTransform = scale = new ScaleTransform();

                DoubleAnimation myDoubleAnimation = new DoubleAnimation();
                myDoubleAnimation.FillBehavior = FillBehavior.HoldEnd;
                myDoubleAnimation.From = 0;
                myDoubleAnimation.To = 1;
                myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(200));
                myDoubleAnimation.AutoReverse = false;

                scale.ScaleX = 1;
                scale.ScaleY = 0;
                scale.BeginAnimation(ScaleTransform.ScaleYProperty, myDoubleAnimation);

                if (!StaysOpen)
                {
                    RegisterMouseDownHandler();
                }
            }

        }

        private void HidePopup()
        {
            ReleaseMouseDownHandler();
            if (_adorner != null)
            {
                _adorner.DisconnectChild();
                if (_adornerLayer != null)
                {
                    _adornerLayer.Remove(_adorner);
                    _adornerLayer = null;
                }
                _adorner = null;
            }
        }

        private void RegisterMouseDownHandler()
        {
            _adornerOfWindow = TreeHelper.GetAncestor<Window>(_adorner);
            if (_adornerOfWindow != null)
            {
                Mouse.AddMouseDownHandler(_adornerOfWindow, OnAdornerOfWindow_PreviewMouseDown);
            }
        }

        private void ReleaseMouseDownHandler()
        {
            if (_adornerOfWindow != null)
            {
                Mouse.RemoveMouseDownHandler(_adornerOfWindow, OnAdornerOfWindow_PreviewMouseDown);
            }
        }

        private void ReleasePlacementTargetEventHandler(FrameworkElement frameworkElement)
        {
            if (frameworkElement == null) return;
            frameworkElement.LostFocus -= OnPlacementTarget_LostFocus;
        }

        private void RegisterPlacementTargetEventHandler(FrameworkElement frameworkElement)
        {
            if (frameworkElement == null) return;
            frameworkElement.LostFocus += OnPlacementTarget_LostFocus;
        }

        private void UpdateAdornerDataContext()
        {
            if (this.Child != null)
            {
                this.Child.DataContext = this.DataContext;
            }
        }
        #endregion

        #region Override Methods
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
        }
        #endregion
    }
}
